package com.andooco.dataq.service.jdbc;

import com.alibaba.fastjson.JSON;
import com.andooco.dataq.service.config.SessionConfig;
import com.andooco.dataq.service.jdbc.dto.JdbcDto;
import com.andooco.dataq.util.CSVUtils;
import com.andooco.dataq.util.CommonConstants;
import com.andooco.dataq.util.DoConnection;
import com.andooco.dataq.util.StatEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.Predicate;

/**
 * 数据库增删操作管理
 *
 * @author andooco
 * 20201125
 */
@Slf4j
@Service("JdbcService")
public class JdbcService {

    @Autowired
    private SessionConfig sessionConfig;

    /**
     * 查询数据源列表
     * 先在缓存中查询，如果缓存中没有数据源信息，加载数据源CSV文件到缓存中
     *
     * @param jdbcName
     * @return
     */
    public List<JdbcDto> findJdbcList(String jdbcName) throws UnsupportedEncodingException {
        log.info("进入JdbcService 查询数据源入口");
        List<JdbcDto> jdbcDtos = getJdbcDtosCached();
        if (StringUtils.isBlank(jdbcName)) {
            return jdbcDtos;
        } else {
            //定义filters
            Predicate<JdbcDto> strFilter = (jdbcDto) -> (jdbcDto.getJdbcName().contains(jdbcName));
            List<JdbcDto> jdbcDtoList = new ArrayList<>();
            jdbcDtos.stream()
                    .filter(strFilter)
                    .forEach((strlist) -> jdbcDtoList.add(strlist));
            return jdbcDtoList;
        }
    }

    /**
     * 查询数据源列表
     * 先在缓存中查询，如果缓存中没有数据源信息，加载数据源CSV文件到缓存中
     *
     * @param jdbcDto
     * @return
     */
    public String saveJdbcList(JdbcDto jdbcDto) throws UnsupportedEncodingException {
        log.info("进入JdbcService 保存数据源入口");
        //保存数据到csv文件中
        String[] dataList = new String[9];
        jdbcDto.setUid(UUID.randomUUID().toString().replaceAll("-", ""));
        List<JdbcDto> jdbcDtos = getJdbcDtosCached();
        //判断连接名是否有重复
        for (JdbcDto jdbcDto1 : jdbcDtos) {
            if (jdbcDto1.getJdbcName().equals(jdbcDto.getJdbcName())) {
                return "连接名重复，保存失败";
            }
        }
        //新增数据到缓存中
        jdbcDtos.add(jdbcDto);
        log.info("追加数据到csv文件中：{}", JSON.toJSONString(jdbcDto));
        convertsToList(dataList, jdbcDto);
        CSVUtils.writeDataToCsvFile(CommonConstants.DATA_ROOT_PATH, CommonConstants.JDBC_PATH, dataList, CSVUtils.HEAD_ARR_DATASOURCE);

        return StatEnum.SUCCESS.getCode();
    }

    /**
     * 删除数据源
     *
     * @param uid
     * @return
     */
    public String delJdbcList(String uid) throws Exception {
        log.info("进入JdbcService 删除数据源入口");
        //保存数据到csv文件中
        String[] dataList = new String[9];

        List<JdbcDto> jdbcDtos = getJdbcDtosCached();
        //判断连接名是否有重复
        //定义filters
        Predicate<JdbcDto> strFilter = (jdbcDto) -> (!jdbcDto.getUid().equals(uid));
        List<JdbcDto> jdbcDtoList = new ArrayList<>();
        jdbcDtos.stream()
                .filter(strFilter)
                .forEach((strlist) -> jdbcDtoList.add(strlist));
        if (jdbcDtos.size() == jdbcDtoList.size()) {
            log.info("未找到匹配数据库");
            return StatEnum.FAIL.getCode();
        }
        jdbcDtos.clear();
        List<String[]> dataListFile = new ArrayList<>();
        for (int i = 0; i < jdbcDtoList.size(); i++) {
            String[] strings = new String[9];
            convertsToList(strings, jdbcDtoList.get(i));
            dataListFile.add(strings);
        }
        log.info("更新csv文件数据：{}", JSON.toJSONString(dataListFile));
        //使用覆盖模式
        CSVUtils.writeDataListToCsvFile(CommonConstants.DATA_ROOT_PATH, CommonConstants.JDBC_PATH, dataListFile, false, CSVUtils.HEAD_ARR_DATASOURCE);
        return StatEnum.SUCCESS.getCode();
    }

    /**
     * 查询数据源列表
     * 先在缓存中查询，如果缓存中没有数据源信息，加载数据源CSV文件到缓存中
     *
     * @param jdbcDto
     * @return
     */
    public String testConnection(JdbcDto jdbcDto) throws UnsupportedEncodingException {
        log.info("进入JdbcService 测试数据库连接入口");
        //判断是mysql数据库还是oracle数据库
        if ("oracle".equals(jdbcDto.getJdbcType()) || "mysql".equals(jdbcDto.getJdbcType())) {
            Connection connection = DoConnection.connection(jdbcDto.getJdbcType(), "单节点", "", jdbcDto.getJdbcUrl(), jdbcDto.getJdbcPort(), jdbcDto.getJdbcSID(), jdbcDto.getJdbcUser(), jdbcDto.getJdbcPassword());
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                } finally {
                    if (connection != null) {
                        connection = null;
                    }
                }
                return StatEnum.SUCCESS.getCode();
            } else {
                return "连接失败，请检查连接参数";
            }
        } else {
            return "目前只支持mysql和oracle数据库";
        }
    }

    /**
     * 查询本行数据源列表
     * 先在缓存中查询，如果缓存中没有数据源信息，加载数据源CSV文件到缓存中
     *
     * @param uid
     * @return
     */
    public String testConnectionThis(String uid) throws UnsupportedEncodingException {
        log.info("进入JdbcService 测试本行数据库连接入口");
        List<JdbcDto> jdbcDtos = getJdbcDtosCached();
        //判断连接名是否有重复
        //定义filters
        Predicate<JdbcDto> strFilter = (jdbcDto) -> (jdbcDto.getUid().equals(uid));
        List<JdbcDto> jdbcDtoList = new ArrayList<>();
        jdbcDtos.stream()
                .filter(strFilter)
                .forEach((strlist) -> jdbcDtoList.add(strlist));
        //理论上jdbcDtoList只有一条
        JdbcDto jdbcDto = jdbcDtoList.get(0);
        log.info("本行数据为：{}", JSON.toJSONString(jdbcDto));
        Connection connection = DoConnection.connection(jdbcDto.getJdbcType(), "单节点", "", jdbcDto.getJdbcUrl(), jdbcDto.getJdbcPort(), jdbcDto.getJdbcSID(), jdbcDto.getJdbcUser(), jdbcDto.getJdbcPassword());
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                if (connection != null) {
                    connection = null;
                }
            }
            return StatEnum.SUCCESS.getCode();
        } else {
            return "连接失败，请检查连接参数";
        }
    }

    /**
     * 获取数据源缓存
     *
     * @return
     * @throws UnsupportedEncodingException
     */
    public List<JdbcDto> getJdbcDtosCached() throws UnsupportedEncodingException {
        List<JdbcDto> jdbcDtos = sessionConfig.embeddedHazelcast().getList(CommonConstants.jdbcList);
        if (jdbcDtos == null || jdbcDtos.size() == 0) {
            //如果缓存jdbc为空，加载数据源CSV文件
            List<String[]> datas = CSVUtils.importCsv(CommonConstants.DATA_ROOT_PATH, CommonConstants.JDBC_PATH);
            convertsToJdbc(datas, jdbcDtos);
        }
        return jdbcDtos;
    }

    /**
     * 把csv文件读取的内容转成bean对象
     *
     * @param datas
     * @param jdbcDtos
     */
    public void convertsToJdbc(List<String[]> datas, List<JdbcDto> jdbcDtos) throws UnsupportedEncodingException {
        for (int i = 0; i < datas.size(); i++) {
            String[] data = datas.get(i);
            JdbcDto jdbcDto = new JdbcDto();
            jdbcDto.setJdbcType(data[0]);
            jdbcDto.setJdbcName(URLDecoder.decode(data[1], "utf-8"));
            jdbcDto.setJdbcUrl(data[2]);
            jdbcDto.setJdbcPort(data[3]);
            jdbcDto.setSidType(data[4]);
            jdbcDto.setJdbcSID(data[5]);
            jdbcDto.setJdbcUser(data[6]);
            jdbcDto.setJdbcPassword(data[7]);
            jdbcDto.setUid(data[8]);
            jdbcDtos.add(jdbcDto);
        }
    }

    /**
     * 把数据转换为数组
     *
     * @param data
     * @param jdbcDto
     */
    public void convertsToList(String[] data, JdbcDto jdbcDto) throws UnsupportedEncodingException {
        data[0] = jdbcDto.getJdbcType();
        data[1] = URLEncoder.encode(jdbcDto.getJdbcName(), "utf-8");
        data[2] = jdbcDto.getJdbcUrl();
        data[3] = jdbcDto.getJdbcPort();
        data[4] = jdbcDto.getSidType();
        data[5] = jdbcDto.getJdbcSID();
        data[6] = jdbcDto.getJdbcUser();
        data[7] = jdbcDto.getJdbcPassword();
        data[8] = jdbcDto.getUid();
    }
}
